﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace Lyon.WinForm.NetMapSuite
{
    /// <summary>
    /// 网络图
    /// </summary>
    public partial class NetMapControl : UserControl
    {
        public Color CaptionTextColor = Color.Blue;
        public Font CaptionTextFont = new Font("黑体", 18);

        public Color DrawBackColor = Color.WhiteSmoke;
        public Color DrawGridColor = Color.LightGray;
        public Size DrawGridSize = new Size(20, 20);

        public Color ItemSelectedColor = Color.Blue;
        public Color ItemBackColor = Color.DarkRed;
        public Color ItemUpDownColor = Color.Green;
        public Font ItemTextFont = new Font("黑体", 12);
        public Color ItemTextColor = Color.Blue;
        public int ItemTextWidth = 90;
        public Size ItemImageSize = new Size(30, 30);

        string _Caption;
        NetMapTool _Tool;
        AbstractTool _ToolAction;
        NetMapItemCollection _Items;
        SelectedItemCollection _SelectedItems;
        List<NetMapItem> _ItemList = new List<NetMapItem>();
        internal List<NetMapItem> _SelectedList = new List<NetMapItem>();

        bool _BeginUpdate = false;



        public NetMapControl()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer
                | ControlStyles.ResizeRedraw
                | ControlStyles.Selectable
                | ControlStyles.UserPaint
                | ControlStyles.SupportsTransparentBackColor
                , true);
            this._ToolAction = new DefaultTool(this);
        }

        
        public string Caption
        {
            get
            {
                return _Caption;
            }
            set
            {
                if (_Caption != value)
                {
                    _Caption = value;
                    this.Refresh();
                }
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (this.DesignMode)
            {
                this.BeginUpdate();
                NetMapItem item1 = this.Items.Add("1", "这是一个演示节点1", new Point(0, 0) + new Size(0,40));
                NetMapItem item2 = this.Items.Add("2", "这是一个演示节点2", new Point(100, 0) + new Size(0, 40));
                NetMapItem item3 = this.Items.Add("3", "这是一个演示节点3", new Point(50, 100) + new Size(0, 40));
                item1.DownItems.Add(item2);
                item2.DownItems.Add(item3);
                item3.DownItems.Add(item1);
                this.EndUpdate();
            }
        }

        public void BeginUpdate()
        {
            if (!_BeginUpdate)
            {
                _BeginUpdate = true;
            }
        }
        public void EndUpdate()
        {
            if (_BeginUpdate)
            {
                _BeginUpdate = false;
                this.Refresh();
            }
            //ListView view;
        }

        public void HorizontalSelectedIndices()
        {
            if (_SelectedList.Count <= 1)
                return;
            bool isChanged = false;
            NetMapItem firstItem = _SelectedList[0];
            for (int i = 1; i < _SelectedList.Count; i++)
            {
                NetMapItem temp = _SelectedList[i];
                if (temp.Location.Y != firstItem.Location.Y)
                {
                    temp.Location = new Point(temp.Location.X, firstItem.Location.Y);
                    isChanged = true;
                }
            }
            if (isChanged)
                this.Refresh();
        }
        public void VerticalSelectedIndices()
        {
            if (_SelectedList.Count <= 1)
                return;
            bool isChanged = false;
            NetMapItem firstItem = _SelectedList[0];
            for (int i = 1; i < _SelectedList.Count; i++)
            {
                NetMapItem temp = _SelectedList[i];
                if (temp.Location.X != firstItem.Location.X)
                {
                    temp.Location = new Point(firstItem.Location.X, temp.Location.Y);
                    isChanged = true;
                }
            }
            if (isChanged)
                this.Refresh();
        }

        [Browsable(false)]
        public SelectedItemCollection SelectedItems
        {
            get
            {
                if (_SelectedItems == null)
                    _SelectedItems = new SelectedItemCollection(this);
                return _SelectedItems;
            }
        }

        [Browsable(false)]
        public NetMapItemCollection Items
        {
            get
            {
                if (_Items == null)
                    _Items = new NetMapItemCollection(this);
                return _Items;
            }
        }

        public Size PageSize
        {
            get
            {
                return this.AutoScrollMinSize;
            }
            set
            {
                this.AutoScrollMinSize = value;
                this.Refresh();
            }
        }

        public NetMapTool Tool
        {
            get { return _Tool; }
            set
            {
                if (_Tool == value)
                    return;
                _Tool = value;
                _ToolAction.Dispose();
                if (_Tool == NetMapTool.Select)
                    _ToolAction = new DefaultTool(this);
                else if (_Tool == NetMapTool.Link)
                    _ToolAction = new LinkTool(this);
                else if (_Tool == NetMapTool.Unlink)
                    _ToolAction = new UnlinkTool(this);
                else if (_Tool == NetMapTool.SelectMove)
                    _ToolAction = new MoveTool(this);
                else if(_Tool == NetMapTool.Pan)
                    _ToolAction = new PanTool(this);
                this.Refresh();
            }
        }

        public event EventHandler SelectedIndexChanged;
        public event EventHandler<NetMapLinkEventArgs> Linked;
        public event EventHandler<NetMapLinkEventArgs> UnLinked;
        public event EventHandler<NetMapItemEventArgs> ItemDoubleClick;
        public event EventHandler SelectedLocationChanged;

        internal void OnSelectItemsChanged(EventArgs e)
        {
            if (SelectedIndexChanged != null)
            {
                SelectedIndexChanged(this, e);
            }
        }
        internal void OnLinked(NetMapLinkEventArgs e)
        {
            if (Linked != null)
            {
                Linked(this, e);
            }
        }
        internal void OnUnLinked(NetMapLinkEventArgs e)
        {
            if (UnLinked != null)
            {
                UnLinked(this, e);
            }
        }
        internal void OnItemDoubleClick(NetMapItemEventArgs e)
        {
            if (ItemDoubleClick != null)
                ItemDoubleClick(this, e);
        }
        internal void OnSelectedLocationChanged(EventArgs e)
        {
            if (SelectedLocationChanged != null)
                SelectedLocationChanged(this, e);
        }

        public override void Refresh()
        {
            if (_BeginUpdate)
                return;
            base.Refresh();
        }

        #region 界面绘制处理
        protected override void OnScroll(ScrollEventArgs se)
        {
            base.OnScroll(se);
            this.Refresh();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            try
            {
                e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
                e.Graphics.FillRectangle(new SolidBrush(DrawBackColor), DrawDiaplayRectangle);
                DrawGrid(e.Graphics);
                if (!String.IsNullOrEmpty(_Caption))
                {
                    e.Graphics.DrawString(_Caption, CaptionTextFont, new SolidBrush(CaptionTextColor), new Point(10, 10));
                }
                _ToolAction.Draw(e.Graphics);
                //Draw(e.Graphics);
            }
            catch (Exception ex)
            {
                e.Graphics.DrawString(ex.ToString(), this.Font, Brushes.Red, e.ClipRectangle);
            }
        }

        private void DrawGrid(Graphics graphics)
        {
            Pen pen = new Pen(DrawGridColor);
            for (int i = 0; i <= this.DrawRectangle.Width; i += DrawGridSize.Width)
            {
                Point p1 = new Point(i, this.DrawRectangle.Top);
                Point p2 = new Point(i, this.DrawRectangle.Bottom);

                //graphics.DrawString(i.ToString(), this.Font, Brushes.Orange, p1.X, this.DrawRectangle.Top + ScrollSize.Height);
                graphics.DrawLine(pen, p1, p2);
            }
            for (int i = 0; i <= this.DrawRectangle.Height; i += DrawGridSize.Height)
            {
                Point p1 = new Point(this.DrawRectangle.Left, i);
                Point p2 = new Point(this.DrawRectangle.Right, i);

                //graphics.DrawString(i.ToString(), this.Font, Brushes.Orange, this.DrawRectangle.Left + ScrollSize.Width, p1.Y);
                graphics.DrawLine(pen, p1, p2);
            }
        }
        
        void DrawArrow(Graphics g, Point from, Point to, Color linkColor)
        {
            to = ParentPoint(from, to, 15);
            float arrowWidth = 10.0f;
            float theta = 0.423f;
            int[] xPoints = new int[3];
            int[] yPoints = new int[3];
            float[] vecLine = new float[2];
            float[] vecLeft = new float[2];
            float fLength;
            float th;
            float ta;
            float baseX, baseY;

            Point[] dPoints = new Point[3];

            xPoints[0] = to.X;
            yPoints[0] = to.Y;
            dPoints[0] = new Point(to.X, to.Y);


            vecLine[0] = (float)xPoints[0] - from.X;
            vecLine[1] = (float)yPoints[0] - from.Y;

            vecLeft[0] = -vecLine[1];
            vecLeft[1] = vecLine[0];


            fLength = (float)Math.Sqrt(vecLine[0] * vecLine[0] + vecLine[1] * vecLine[1]);
            th = arrowWidth / (2.0f * fLength);
            ta = arrowWidth / (2.0f * ((float)Math.Tan(theta) / 2.0f) * fLength);


            baseX = ((float)xPoints[0] - ta * vecLine[0]);
            baseY = ((float)yPoints[0] - ta * vecLine[1]);


            xPoints[1] = (int)(baseX + th * vecLeft[0]);
            yPoints[1] = (int)(baseY + th * vecLeft[1]);
            xPoints[2] = (int)(baseX - th * vecLeft[0]);
            yPoints[2] = (int)(baseY - th * vecLeft[1]);

            dPoints[1] = new Point(xPoints[1], yPoints[1]);
            dPoints[2] = new Point(xPoints[2], yPoints[2]);

            Pen thick_pen = new Pen(linkColor, 2);
            Point p1 = new Point(from.X, from.Y);
            Point p2 = new Point((int)baseX, (int)baseY);
            g.DrawLine(thick_pen, p1, p2);
            g.FillPolygon(new SolidBrush(linkColor), dPoints);
        }
        Point ParentPoint(Point from, Point to, int radius)
        {
            Point returnValue = new Point();
            float y1 = to.Y - from.Y;
            float x1 = to.X - from.X;
            double alpha = 0;

            if (x1 < 0)
                alpha = Math.Atan(y1 / x1) + Math.PI;
            if (x1 > 0)
                alpha = Math.Atan(y1 / x1);
            if (x1 == 0)
            {
                if (y1 > 0)
                    alpha = Math.PI / 2;
                else if (y1 < 0)
                    alpha = -Math.PI / 2;
                else
                    alpha = 0;
            }
            returnValue.Y = Convert.ToInt32(y1 - radius * Math.Sin(alpha)) + from.Y;
            returnValue.X = Convert.ToInt32(x1 - radius * Math.Cos(alpha)) + from.X;

            return returnValue;
        }

        void DrawCenterText(Graphics g, Rectangle rectangle, Font font, Color color,string text)
        {
            StringFormat temp = new StringFormat(StringFormat.GenericTypographic);
            temp.Alignment = StringAlignment.Center;
            //temp.Trimming = StringTrimming.EllipsisCharacter;

            SizeF size = g.MeasureString(text, font, rectangle.Width, temp);

            g.DrawString(text, font, new SolidBrush(color), rectangle, temp);
            //g.DrawRectangle(new Pen(color), rectangle);
        }
        Size GetDrawTextSize(Graphics g,Font font,int width,string text)
        {
            StringFormat temp = new StringFormat(StringFormat.GenericDefault);
            temp.Alignment = StringAlignment.Center;
            //temp.Trimming = StringTrimming.EllipsisCharacter;

            SizeF size = g.MeasureString(text, font, width, temp);
            //double t = Math.Round(size.Height, 0);
            return new Size((int)size.Width, (int)size.Height);
        }
        #endregion

        #region 鼠标事件处理
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);
            NetMapItem pickItem = PickItem(e.Location + ScrollSize);
            if (pickItem != null)
            {
                OnItemDoubleClick(new NetMapItemEventArgs(pickItem));
            }
        }
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            this.Refresh();
        }
        #endregion

        #region 私有方法
        Rectangle UpdateScrollRect
        {
            get
            {
                return new Rectangle(5, 5, this.ClientRectangle.Width - 10, this.ClientRectangle.Height - 10);
            }
        }
        NetMapItem PickItem(Point p)
        {
            return _ItemList.Find(delegate(NetMapItem item)
            {
                return item.Rectangle.Contains(p);
            });
        }
        List<NetMapItem> PickItems(Rectangle rect)
        {
            return _ItemList.FindAll(delegate(NetMapItem item)
            {
                return rect.Contains(item.Rectangle);
            });
        }
        Rectangle GetSelectRectangle()
        {
            Rectangle rect = Rectangle.Empty;
            foreach (NetMapItem item in _ItemList)
            {
                if (item.Selected)
                {
                    rect = Rectangle.Union(item.Rectangle, rect);
                }
            }
            return rect;
        }
        private Size UpdateScrollPostion(Point mouseLocation)
        {
            Rectangle rect = UpdateScrollRect;
            if (!rect.Contains(mouseLocation))
            {
                int w = 0;
                int h = 0;
                if (mouseLocation.X > rect.Right)
                    w = mouseLocation.X - rect.Right;
                else if (mouseLocation.X < rect.Left)
                    w = mouseLocation.X - rect.Left;
                if (mouseLocation.Y > rect.Bottom)
                    h = mouseLocation.Y - rect.Bottom;
                else if (mouseLocation.Y < rect.Top)
                    h = mouseLocation.Y - rect.Top;
                if (w != 0 || h != 0)
                {
                    Size size = new Size(w, h);
                    ScrollSize += size;
                    return size;
                }
            }
            return Size.Empty;
        }
        internal Rectangle DrawRectangle
        {
            get
            {
                return new Rectangle(this.ClientRectangle.Location, this.AutoScrollMinSize);
            }
        }
        internal Rectangle DrawDiaplayRectangle
        {
            get
            {
                return new Rectangle(this.ClientRectangle.Location + this.ScrollSize,
                    new Size(PageSize.Width < this.ClientSize.Width ? PageSize.Width : this.ClientSize.Width
                        , PageSize.Height < this.ClientSize.Height ? PageSize.Height : this.ClientSize.Height));
            }
        }
        internal Point DrawDiaplayLocation
        {
            get
            {
                return this.ClientRectangle.Location + this.ScrollSize;
            }
            set
            {
                this.ScrollSize = new Size(value.X - this.ClientRectangle.Location.X, value.Y - this.ClientRectangle.Location.Y);
            }
        }
        private Size ScrollSize
        {
            get
            {
                return new Size(-this.AutoScrollPosition.X, -this.AutoScrollPosition.Y);
            }
            set
            {
                this.AutoScrollPosition = new Point(value.Width, value.Height);
            }
        }
        #endregion

        public class NetMapItemCollection : IEnumerable<NetMapItem>
        {
            private NetMapControl netMapControl;

            internal NetMapItemCollection(NetMapControl netMapControl)
            {
                // TODO: Complete member initialization
                this.netMapControl = netMapControl;
            }

            public NetMapItem Add(string key, string text, Point location)
            {
                NetMapItem item = new NetMapItem();
                item._NetMapControl = netMapControl;
                item.Name = key;
                item.Text = text;
                item.Location = location;
                netMapControl._ItemList.Add(item);
                netMapControl.Refresh();
                return item;
            }
            public NetMapItem Add(string key, string text)
            {
                Rectangle drawDiaplayRectangle = netMapControl.DrawDiaplayRectangle;
                return Add(key, text, drawDiaplayRectangle.Location + new Size(drawDiaplayRectangle.Width / 2, drawDiaplayRectangle.Height / 2));
            }
            public NetMapItem Add(string text)
            {
                return Add("", text);
            }
            public void Remove(NetMapItem item)
            {
                item.UpItems.Clear();
                item.DownItems.Clear();
                item._NetMapControl = null;
                netMapControl._ItemList.Remove(item);
                if (netMapControl._SelectedList.Remove(item))
                    netMapControl.OnSelectItemsChanged(EventArgs.Empty);
                netMapControl.Refresh();
            }
            public void RemoveAt(int index)
            {
                Remove(netMapControl._ItemList[index]);
            }
            public void Clear()
            {
                bool selectChanged = netMapControl._SelectedList.Count > 0;

                netMapControl._ItemList.ForEach(delegate(NetMapItem item)
                {
                    item._NetMapControl = null;
                });
                netMapControl._SelectedList.Clear();
                netMapControl._ItemList.Clear();
                netMapControl.Refresh();

                if (selectChanged)
                    netMapControl.OnSelectItemsChanged(EventArgs.Empty);
            }

            public IEnumerator<NetMapItem> GetEnumerator()
            {
                return netMapControl._ItemList.GetEnumerator();
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return netMapControl._ItemList.GetEnumerator();
            }

            public int IndexOf(NetMapItem item)
            {
                return this.netMapControl._ItemList.IndexOf(item);
            }

            public NetMapItem this[int index]
            {
                get
                {
                    return netMapControl._ItemList[index];
                }
            }
            public NetMapItem this[string key]
            {
                get
                {
                    return netMapControl._ItemList.Find(delegate(NetMapItem item)
                    {
                        return item.Name == key;
                    });
                }
            }
        }

        public class SelectedItemCollection : IEnumerable<NetMapItem>
        {
            private NetMapControl owner;

            public SelectedItemCollection(NetMapControl netMapControl)
            {
                // TODO: Complete member initialization
                this.owner = netMapControl;
            }

            public bool Contains(NetMapItem item)
            {
                if (item._NetMapControl != owner)
                    throw new ArgumentException();
                return item.Selected;
            }
            public void Add(NetMapItem item)
            {
                if (item._NetMapControl != owner)
                    throw new ArgumentException();
                if (!item.Selected)
                {
                    item.Selected = true;
                    this.owner.Refresh();
                    this.owner.OnSelectItemsChanged(EventArgs.Empty);
                }
            }
            public void Remove(NetMapItem item)
            {
                if (item._NetMapControl != owner)
                    throw new ArgumentException();
                if (item.Selected)
                {
                    item.Selected = false;
                    this.owner.Refresh();
                    this.owner.OnSelectItemsChanged(EventArgs.Empty);
                }
            }

            public void Clear()
            {
                if (this.owner._SelectedList.Count == 0)
                    return;
                foreach (NetMapItem item in this.owner._SelectedList.ToArray())
                {
                    item.Selected = false;
                }
                this.owner.Refresh();
                this.owner.OnSelectItemsChanged(EventArgs.Empty);
            }

            public int Count
            {
                get
                {
                    return owner._SelectedList.Count;
                }
            }

            public NetMapItem this[int index]
            {
                get
                {
                    return this.owner._SelectedList[index];
                }
            }

            public IEnumerator<NetMapItem> GetEnumerator()
            {
                foreach (NetMapItem item in this.owner._SelectedList.ToArray())
                    yield return item;
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }

        private class DefaultTool : AbstractTool
        {
            Point _MoveStartPoint;
            Point _MoveEndPoint;

            Rectangle _SelectBox
            {
                get
                {
                    int x1, y1, x2, y2;
                    if (_MoveStartPoint.X < _MoveEndPoint.X)
                    {
                        x1 = _MoveStartPoint.X;
                        x2 = _MoveEndPoint.X;
                    }
                    else
                    {
                        x2 = _MoveStartPoint.X;
                        x1 = _MoveEndPoint.X;
                    }
                    if (_MoveStartPoint.Y < _MoveEndPoint.Y)
                    {
                        y1 = _MoveStartPoint.Y;
                        y2 = _MoveEndPoint.Y;
                    }
                    else
                    {
                        y2 = _MoveStartPoint.Y;
                        y1 = _MoveEndPoint.Y;
                    }
                    return new Rectangle(x1, y1, x2 - x1, y2 - y1);
                }
            }

            public DefaultTool(NetMapControl owner)
                : base(owner)
            {
                _Owner.Cursor = Cursors.Default;
                _Owner.MouseDown += new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove += new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp += new MouseEventHandler(owner_MouseUp);
            }
            public override void Dispose()
            {
                _Owner.MouseDown -= new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove -= new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp -= new MouseEventHandler(owner_MouseUp);
            }
            public override void Draw(Graphics graphics)
            {
                base.Draw(graphics);
                if (!_SelectBox.Size.IsEmpty)
                {
                    Pen pen = new Pen(_Owner.ItemSelectedColor, 2);
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                    graphics.DrawRectangle(pen, new Rectangle(_SelectBox.Location, _SelectBox.Size));
                }
            }


            void owner_MouseUp(object sender, MouseEventArgs e)
            {
                if (_SelectBox.IsEmpty)
                    return;

                bool changed = false;
                if (IsMultiSelect)
                {
                    foreach (NetMapItem item in _Owner._ItemList)
                    {
                        if (_SelectBox.Contains(item.Rectangle))
                        {
                            item.Selected = !item.Selected;
                            changed = true;
                        }
                    }
                }
                else
                {
                    foreach (NetMapItem item in _Owner._ItemList)
                    {
                        if (item.Selected != _SelectBox.Contains(item.Rectangle))
                        {
                            item.Selected = !item.Selected;
                            changed = true;
                        }
                    }
                }
                _MoveStartPoint = Point.Empty;
                _MoveEndPoint = Point.Empty;
                _Owner.Refresh();
                if (changed)
                    _Owner.OnSelectItemsChanged(EventArgs.Empty);
            }

            void owner_MouseMove(object sender, MouseEventArgs e)
            {

                Point realLocation = e.Location + _Owner.ScrollSize;
                if (!_SelectBox.IsEmpty)
                {
                    realLocation += _Owner.UpdateScrollPostion(e.Location);
                    _MoveEndPoint = realLocation;
                    _Owner.Refresh();
                }
            }

            void owner_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    Point realLocation = e.Location + _Owner.ScrollSize;
                    NetMapItem item = _Owner.PickItem(realLocation);
                    if (item == null)
                    {
                        _MoveStartPoint = realLocation;
                        _MoveEndPoint = realLocation;
                        _Owner.Refresh();
                    }
                    else
                    {
                        bool selectChanged = false;
                        if (IsMultiSelect)
                        {
                            item.Selected = !item.Selected;
                            selectChanged = true;
                        }
                        else
                        {
                            if (!(_Owner._SelectedList.Count == 1 && item.Selected))
                            {
                                foreach (NetMapItem temp in _Owner._ItemList)
                                    temp.Selected = item == temp;
                                selectChanged = true;
                            }
                        }
                        if (selectChanged)
                        {
                            _Owner.Refresh();
                            _Owner.OnSelectItemsChanged(e);
                        }
                    }
                }
            }

            private bool IsMultiSelect
            {
                get
                {
                    return (Control.ModifierKeys & Keys.Control) == Keys.Control;
                }
            }
        }
        private class MoveTool : AbstractTool
        {
            Point _MoveStartPoint;
            Point _MoveEndPoint;
            NetMapItem _MoveItem;
            Rectangle _SelectBox
            {
                get
                {
                    int x1, y1, x2, y2;
                    if (_MoveStartPoint.X < _MoveEndPoint.X)
                    {
                        x1 = _MoveStartPoint.X;
                        x2 = _MoveEndPoint.X;
                    }
                    else
                    {
                        x2 = _MoveStartPoint.X;
                        x1 = _MoveEndPoint.X;
                    }
                    if (_MoveStartPoint.Y < _MoveEndPoint.Y)
                    {
                        y1 = _MoveStartPoint.Y;
                        y2 = _MoveEndPoint.Y;
                    }
                    else
                    {
                        y2 = _MoveStartPoint.Y;
                        y1 = _MoveEndPoint.Y;
                    }
                    return new Rectangle(x1, y1, x2 - x1, y2 - y1);
                }
            }

            public MoveTool(NetMapControl owner)
                : base(owner)
            {
                _Owner.Cursor = Cursors.Default;
                _Owner.MouseDown += new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove += new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp += new MouseEventHandler(owner_MouseUp);
            }
            public override void Dispose()
            {
                _Owner.MouseDown -= new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove -= new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp -= new MouseEventHandler(owner_MouseUp);
            }
            public override void Draw(Graphics graphics)
            {
                base.Draw(graphics);
                if (_MoveItem == null && !_SelectBox.Size.IsEmpty)
                {
                    Pen pen = new Pen(_Owner.ItemSelectedColor, 2);
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                    graphics.DrawRectangle(pen, new Rectangle(_SelectBox.Location, _SelectBox.Size));
                }
            }

            void owner_MouseUp(object sender, MouseEventArgs e)
            {
                if (_MoveStartPoint.IsEmpty && _MoveEndPoint.IsEmpty)
                    return;
                Point realLocation = e.Location + _Owner.ScrollSize;
                if (_MoveItem != null)
                {
                    Size moveTo = new Size(realLocation.X - _MoveStartPoint.X, realLocation.Y - _MoveStartPoint.Y);
                    _MoveItem = null;
                    //_Owner.Cursor = Cursors.Default;
                    _MoveStartPoint = Point.Empty;
                    _MoveEndPoint = Point.Empty;

                    if (!moveTo.IsEmpty)
                    {
                        bool refresh = false;
                        foreach (NetMapItem item in _Owner._SelectedList)
                        {
                            Rectangle itemRect = item.Rectangle;
                            Rectangle rect = NearestRect.GetNearestRect(_Owner.DrawRectangle, itemRect);
                            if (rect != itemRect)
                            {
                                item.Location += new Size(rect.X - itemRect.X, rect.Y - itemRect.Y);
                                refresh = true;
                            }
                        }
                        if (refresh)
                            _Owner.Refresh();
                        _Owner.OnSelectedLocationChanged(e);
                    }
                }
                else
                {
                    bool changed = false;
                    if (IsMultiSelect)
                    {
                        foreach (NetMapItem item in _Owner._ItemList)
                        {
                            if (_SelectBox.Contains(item.Rectangle))
                            {
                                item.Selected = !item.Selected;
                                changed = true;
                            }
                        }
                    }
                    else
                    {
                        foreach (NetMapItem item in _Owner._ItemList)
                        {
                            if (item.Selected != _SelectBox.Contains(item.Rectangle))
                            {
                                item.Selected = !item.Selected;
                                changed = true;
                            }
                        }
                    }
                    _MoveStartPoint = Point.Empty;
                    _MoveEndPoint = Point.Empty;
                    _Owner.Refresh();
                    if (changed)
                        _Owner.OnSelectItemsChanged(EventArgs.Empty);
                }
            }

            void owner_MouseMove(object sender, MouseEventArgs e)
            {
                Point realLocation = e.Location + _Owner.ScrollSize;
                if (_MoveItem != null)
                {
                    realLocation += _Owner.UpdateScrollPostion(e.Location);
                    Size moveTo = new Size(realLocation.X - _MoveEndPoint.X, realLocation.Y - _MoveEndPoint.Y);

                    foreach (NetMapItem item in _Owner._SelectedList)
                        item.Location += moveTo;
                    _MoveEndPoint = realLocation;
                    _Owner.Refresh();
                    //if (GetDistance(_MoveStartPoint, _MoveEndPoint) > 1)
                    //    _Owner.Cursor = Cursors.NoMove2D;
                }
                else if (!_SelectBox.IsEmpty)
                {
                    realLocation += _Owner.UpdateScrollPostion(e.Location);
                    _MoveEndPoint = realLocation;
                    _Owner.Refresh();
                }
            }

            void owner_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button != MouseButtons.Left)
                    return;

                Point realLocation = e.Location + _Owner.ScrollSize;
                NetMapItem item = _Owner.PickItem(realLocation);
                if (item == null)
                {
                    _MoveStartPoint = realLocation;
                    _MoveEndPoint = realLocation;
                }
                else
                {
                    bool selectChanged = false;
                    if (IsMultiSelect)
                    {
                        item.Selected = !item.Selected;
                        selectChanged = true;
                    }
                    else
                    {
                        if (!item.Selected)
                        {
                            foreach (NetMapItem temp in _Owner._ItemList)
                                temp.Selected = item == temp;
                            selectChanged = true;
                        }
                    }
                    if (item.Selected)
                    {
                        _MoveItem = item;
                        _MoveStartPoint = realLocation;
                        _MoveEndPoint = realLocation;
                    }
                    _Owner.Refresh();
                    if (selectChanged)
                        _Owner.OnSelectItemsChanged(e);
                }
            }

            private bool IsMultiSelect
            {
                get
                {
                    return (Control.ModifierKeys & Keys.Control) == Keys.Control;
                }
            }
            private decimal GetDistance(Point p1, Point p2)
            {
                int x = p1.X - p2.X;
                int y = p1.Y - p2.Y;
                return Convert.ToDecimal(Math.Sqrt((x * x)+ (y * y)));
            }
        }
        private class LinkTool : AbstractTool
        {
            Point _MoveStartPoint;
            Point _MoveEndPoint;
            NetMapItem _LinkItem;

            public LinkTool(NetMapControl owner)
                : base(owner)
            {
                _Owner.Cursor = new Cursor(new System.IO.MemoryStream(NetMapControlResource.Link));
                _Owner.MouseDown += new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove += new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp += new MouseEventHandler(owner_MouseUp);
            }
            public override void Dispose()
            {
                _Owner.MouseDown -= new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove -= new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp -= new MouseEventHandler(owner_MouseUp);
            }

            public override void Draw(Graphics graphics)
            {
                base.Draw(graphics);
                if (_LinkItem != null && _MoveStartPoint != _MoveEndPoint)
                {
                    _Owner.DrawArrow(graphics, _MoveStartPoint, _MoveEndPoint, _Owner.ItemSelectedColor);
                }
            }


            void owner_MouseUp(object sender, MouseEventArgs e)
            {
                if (_MoveStartPoint.IsEmpty && _MoveEndPoint.IsEmpty)
                    return;
                _Owner.BeginUpdate();
                if (_LinkItem != null)
                {
                    Point realLocation = e.Location + _Owner.ScrollSize;
                    NetMapItem item = _Owner.PickItem(realLocation);
                    if (item != null && item != _LinkItem)
                        _LinkItem.DownItems.Add(item);
                    _LinkItem = null;

                }
                _MoveStartPoint = Point.Empty;
                _MoveEndPoint = Point.Empty;
                _Owner.EndUpdate();
            }
            void owner_MouseMove(object sender, MouseEventArgs e)
            {
                if (_LinkItem != null)
                {
                    Point realLocation = e.Location + _Owner.ScrollSize;
                    _Owner.BeginUpdate();
                    _MoveEndPoint = realLocation;
                    _Owner.EndUpdate();
                }
            }
            void owner_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    Point realLocation = e.Location + _Owner.ScrollSize;
                    _LinkItem = _Owner.PickItem(realLocation);
                    if (_LinkItem != null)
                    {
                        _MoveStartPoint = realLocation;
                        _MoveEndPoint = realLocation;
                    }
                }
            }

            Rectangle GetUnLinkRect(NetMapItem item, NetMapItem downItem)
            {
                Size size = new Size(20, 20);
                return new Rectangle(new Point((item.CenterLocation.X + downItem.CenterLocation.X) / 2 - size.Width / 2, (item.CenterLocation.Y + downItem.CenterLocation.Y) / 2 - size.Height / 2), size);
            }
        }
        private class UnlinkTool : AbstractTool
        {
            int Distance = 5;

            public UnlinkTool(NetMapControl owner)
                : base(owner)
            {
                _Owner.Cursor = new Cursor(new System.IO.MemoryStream(NetMapControlResource.Unlink));
                _Owner.MouseClick += new MouseEventHandler(_Owner_MouseClick);
            }
            public override void Dispose()
            {
                _Owner.MouseClick -= new MouseEventHandler(_Owner_MouseClick);
            }

            void _Owner_MouseClick(object sender, MouseEventArgs e)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    Point realPoint = e.Location + _Owner.ScrollSize;

                    foreach (NetMapItem item in _Owner._ItemList)
                    {
                        foreach (NetMapItem downItem in item.DownItems)
                        {
                            decimal d = GetDistance(item.CenterLocation, downItem.CenterLocation, realPoint);
                            if (d <= Distance)
                            {
                                item.DownItems.Remove(downItem);
                                return;
                            }
                        }
                    }
                }
            }
            
            private decimal GetDistance(Point p1,Point p2, Point p)
            {
                if (p2.X == p1.X)
                    return Math.Abs(p.X - p1.X);
                double k = (p2.Y - p1.Y) / Convert.ToDouble(p2.X - p1.X);
                if (k == 0)
                    return Math.Abs(p.Y - p1.Y);
                double fenzi = Math.Abs(k * p.X - p.Y - k * p1.X + p1.Y);
                double fenmu = Math.Sqrt(k * k + 1);
                return Convert.ToDecimal(fenzi / fenmu);

            }
        }
        private class PanTool : AbstractTool
        {
            bool _IsMove = false;
            Point _MoveStartPoint;
            Point _MoveEndPoint;

            public PanTool(NetMapControl owner)
                : base(owner)
            {
                _Owner.Cursor = Cursors.SizeAll;
                _Owner.MouseDown += new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove += new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp += new MouseEventHandler(owner_MouseUp);
            }
            public override void Dispose()
            {
                _Owner.MouseDown -= new MouseEventHandler(owner_MouseDown);
                _Owner.MouseMove -= new MouseEventHandler(owner_MouseMove);
                _Owner.MouseUp -= new MouseEventHandler(owner_MouseUp);
                _Owner.Cursor = Cursors.Default;
            }

            void owner_MouseUp(object sender, MouseEventArgs e)
            {
                _IsMove = false;
            }
            void owner_MouseMove(object sender, MouseEventArgs e)
            {
                
                if (_IsMove)
                {
                    Point realLocation = e.Location + _Owner.ScrollSize;
                    Size moveTo = new Size(realLocation.X - _MoveEndPoint.X, realLocation.Y - _MoveEndPoint.Y);
                    _Owner.ScrollSize -= moveTo;
                    _Owner.Refresh();
                }
            }
            void owner_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    Point realLocation = e.Location + _Owner.ScrollSize;
                    _MoveStartPoint = realLocation;
                    _MoveEndPoint = realLocation;
                    _IsMove = true;
                }
            }
        }

        private abstract class AbstractTool : System.IDisposable
        {
            protected NetMapControl _Owner;
            NetMapItem _ShowToolTipItem;

            public AbstractTool(NetMapControl owner)
            {
                _Owner = owner;
                _Owner.MouseMove += new MouseEventHandler(_Owner_MouseMove);
            }

            void _Owner_MouseMove(object sender, MouseEventArgs e)
            {
                Point realLocation = e.Location + _Owner.ScrollSize;
                NetMapItem item = _Owner.PickItem(realLocation);
                if (_ShowToolTipItem != item)
                {
                    _ShowToolTipItem = item;
                    if (_ShowToolTipItem == null)
                        _Owner.toolTip1.Hide(_Owner);
                    else
                        _Owner.toolTip1.Show(_ShowToolTipItem.Text, _Owner);
                }
            }

            virtual public void Dispose()
            {
                _Owner.MouseMove -= new MouseEventHandler(_Owner_MouseMove);
                _Owner = null;
            }

            virtual public void DrawItemDown(Graphics g, NetMapItem item, NetMapItem downItem)
            {
                Point p1 = item.CenterLocation;
                Point p2 = downItem.CenterLocation;
                if (p1 != p2)
                {
                    _Owner.DrawArrow(g, p1, p2, _Owner.ItemUpDownColor);
                }
            }
            virtual public void DrawItem(Graphics g, NetMapItem item)
            {
                if (item.Selected)
                {
                    g.FillRectangle(new SolidBrush(_Owner.ItemSelectedColor), item.Rectangle);
                    //g.DrawRectangle(new Pen(Color.WhiteSmoke, 2), item.Rectangle);
                }
                if (item.Image == null)
                {
                    g.DrawEllipse(new Pen(Color.Black, 4), item.ImageRectangle);
                    if (item.Selected)
                        g.FillEllipse(new SolidBrush(_Owner.ItemSelectedColor), item.ImageRectangle);
                    else
                        g.FillEllipse(new SolidBrush(_Owner.ItemBackColor), item.ImageRectangle);
                   
                }
                else
                {
                    g.DrawImage(item.Image, item.ImageRectangle);  
                }
                if (item.Selected)
                    _Owner.DrawCenterText(g, item.TextRectangle, _Owner.ItemTextFont, Color.WhiteSmoke, item.Text);
                else
                    _Owner.DrawCenterText(g, item.TextRectangle, _Owner.ItemTextFont, _Owner.ItemTextColor, item.Text);
            }
            virtual public void Draw(Graphics graphics)
            {
                for (int i = _Owner._ItemList.Count - 1; i >= 0; i--)
                {
                    NetMapItem item = _Owner._ItemList[i];
                    IntTextHeight(graphics, item);
                }
                for (int i = _Owner._ItemList.Count - 1; i >= 0; i--)
                {
                    NetMapItem item = _Owner._ItemList[i];
                    foreach (NetMapItem downItem in item.DownItems)
                    {
                        DrawItemDown(graphics, item, downItem);
                    }
                }
                for (int i = _Owner._ItemList.Count - 1; i >= 0; i--)
                {
                    NetMapItem item = _Owner._ItemList[i];
                    DrawItem(graphics, item);
                }
            }
            private void IntTextHeight(Graphics g, NetMapItem item)
            {
                if (item._TextSize.IsEmpty)
                    item._TextSize = _Owner.GetDrawTextSize(g, _Owner.ItemTextFont, _Owner.ItemTextWidth, item.Text);
            }
        }
    }
    /// <summary>
    /// 表示 NetMapControl 控件中Linked 或 Unlinked事件的方法
    /// </summary>
    public class NetMapLinkEventArgs : EventArgs
    {
        NetMapItem _Item;
        NetMapItem _DownItem;

        public NetMapLinkEventArgs(NetMapItem item, NetMapItem downItem)
        {
            _Item = item;
            _DownItem = downItem;
        }

        public NetMapItem Item
        {
            get
            {
                return _Item;
            }
        }
        public NetMapItem DownItem
        {
            get
            {
                return _DownItem;
            }
        }
    }
    /// <summary>
    /// 表示将对 NetMapControl 的以下事件进行处理的方法:ItemDoubleClick
    /// </summary>
    public class NetMapItemEventArgs : EventArgs
    {
        NetMapItem _Item;

        public NetMapItemEventArgs(NetMapItem item)
        {
            _Item = item;
        }

        public NetMapItem Item
        {
            get
            {
                return _Item;
            }
        }
    }

    /// <summary>
    /// 表示 NetMapControl 控件中的项。
    /// </summary>
    public class NetMapItem
    {
        internal NetMapControl _NetMapControl;
        List<NetMapItem> _UpList = new List<NetMapItem>();
        List<NetMapItem> _DownList = new List<NetMapItem>();
        UpCollection _Ups;
        DownCollection _Downs;

        string _Text;
        Image _Image;
        Point _Location;
        bool _Selected;


        public NetMapControl NetMapControl
        {
            get
            {
                return _NetMapControl;
            }
        }
        public string Name { get; set; }
        public string Text
        {
            get { return _Text; }
            set
            {
                if (value == null)
                    value = String.Empty;
                if (_Text == value)
                    return;
                _Text = value;
                _NetMapControl.Refresh();
            }
        }
        public Image Image
        {
            get { return _Image; }
            set
            {
                if (_Image == value)
                    return;
                _Image = value;
                _NetMapControl.Refresh();
            }
        }
        public Point Location
        {
            get { return _Location; }
            internal set
            {
                if (_Location == value)
                    return;
                _Location = value;
            }
        }
        public object Tag { get; set; }
        public bool Selected
        {
            get { return _Selected; }
            internal set
            {
                if (_Selected == value)
                    return;
                _Selected = value;
                if (_Selected)
                    _NetMapControl._SelectedList.Add(this);
                else
                    _NetMapControl._SelectedList.Remove(this);
            }
        }

        public int Index
        {
            get
            {
                return _NetMapControl.Items.IndexOf(this);
            }
        }

        public void EnsureVisible()
        {
            Rectangle rect = NearestRect.GetNearestRect(_NetMapControl.DrawDiaplayRectangle, this.Rectangle);
            Size moveTo = new Size(rect.Location.X - this.Location.X, rect.Location.Y - this.Location.Y);
            if (!moveTo.IsEmpty)
            {
                _NetMapControl.DrawDiaplayLocation -= moveTo;
                _NetMapControl.Refresh();
            }
        }

        int _Border = 2;
        internal Size _TextSize = Size.Empty;

        internal Rectangle Rectangle
        {
            get
            {
                //Rectangle rect = ImageRectangle;
                //return new Rectangle(rect.X - _Border, rect.Y - _Border, rect.Width + _Border * 2, rect.Height + _Border * 2);
                //return new Rectangle(Location, _NetMapControl.ItemImageSize + new Size(_Border, _Border));
                int imageWidth = _NetMapControl.ItemImageSize.Width + _Border * 2;
                if (imageWidth > _TextSize.Width)
                {
                    int imageHeight = _NetMapControl.ItemImageSize.Height + _Border * 2;
                    return new Rectangle(Location + new Size((_NetMapControl.ItemTextWidth - imageWidth) / 2, 0)
                        , new Size(imageWidth, imageHeight + _Border * 2 + _TextSize.Height));
                }
                else
                {
                    int imageHeight = _NetMapControl.ItemImageSize.Height + _Border * 2;
                    return new Rectangle(Location + new Size((_NetMapControl.ItemTextWidth - _TextSize.Width) / 2, 0)
                        , new Size(_TextSize.Width, imageHeight + _Border * 2 + _TextSize.Height));
                }
            }
        }
        internal Point CenterLocation
        {
            get
            {
                Rectangle rect = ImageRectangle;
                return rect.Location + new Size(rect.Width / 2, rect.Height / 2);
                //return Location + new Size(_NetMapControl.ItemSize.Width / 2, _NetMapControl.ItemSize.Height / 2);
            }
        }
        internal Rectangle ImageRectangle
        {
            get
            {
                return new Rectangle(Location + new Size((_NetMapControl.ItemTextWidth - _NetMapControl.ItemImageSize.Width) / 2, _Border), _NetMapControl.ItemImageSize);
            }
        }
        internal Rectangle TextRectangle
        {
            get
            {
                return new Rectangle(Location + new Size((_NetMapControl.ItemTextWidth - _TextSize.Width)/2, _Border + _NetMapControl.ItemImageSize.Height + _Border)
                    , _TextSize);
            }
        }

        public UpCollection UpItems
        {
            get
            {
                if (_Ups == null)
                    _Ups = new UpCollection(this);
                return _Ups;
            }
        }
        public DownCollection DownItems
        {
            get
            {
                if (_Downs == null)
                    _Downs = new DownCollection(this);
                return _Downs;
            }
        }

        protected void OnDownAdded(NetMapItem item)
        {
            _NetMapControl.OnLinked(new NetMapLinkEventArgs(this, item));
            _NetMapControl.Refresh();
        }
        protected void OnDownRemoved(NetMapItem item)
        {
            _NetMapControl.OnUnLinked(new NetMapLinkEventArgs(this, item));
            _NetMapControl.Refresh();
        }

        public class UpCollection : IEnumerable<NetMapItem>
        {
            private NetMapItem netMapItem;

            public UpCollection(NetMapItem netMapItem)
            {
                // TODO: Complete member initialization
                this.netMapItem = netMapItem;
            }

            public IEnumerator<NetMapItem> GetEnumerator()
            {
                foreach (NetMapItem item in this.netMapItem._UpList.ToArray())
                    yield return item;
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            public bool Contains(NetMapItem item)
            {
                return netMapItem._UpList.Contains(item);
            }

            public void Add(NetMapItem item)
            {
                if (item == netMapItem)
                    throw new ArgumentException();
                if (item._NetMapControl != netMapItem._NetMapControl)
                    throw new ArgumentException();
                if (!netMapItem._UpList.Contains(item))
                {
                    netMapItem._UpList.Add(item);
                    item._DownList.Add(netMapItem);
                    item.OnDownAdded(netMapItem);
                }

            }
            public void Remove(NetMapItem item)
            {
                if (item == netMapItem)
                    throw new ArgumentException();
                if (item._NetMapControl != netMapItem._NetMapControl)
                    throw new ArgumentException();
                if (netMapItem._UpList.Contains(item))
                {
                    netMapItem._UpList.Remove(item);
                    item._DownList.Remove(netMapItem);
                    item.OnDownRemoved(netMapItem);
                }
            }
            public void Clear()
            {
                foreach (NetMapItem item in netMapItem._UpList.ToArray())
                {
                    netMapItem._UpList.Remove(item);
                    item._DownList.Remove(netMapItem);
                    item.OnDownRemoved(netMapItem);
                }
            }

            public int Count
            {
                get
                {
                    return this.netMapItem._UpList.Count;
                }
            }
        }

        public class DownCollection : IEnumerable<NetMapItem>
        {
            private NetMapItem netMapItem;

            public DownCollection(NetMapItem netMapItem)
            {
                // TODO: Complete member initialization
                this.netMapItem = netMapItem;
            }

            public IEnumerator<NetMapItem> GetEnumerator()
            {
                foreach (NetMapItem item in this.netMapItem._DownList.ToArray())
                    yield return item;
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
            public bool Contains(NetMapItem item)
            {
                return netMapItem._DownList.Contains(item);
            }
            public void Add(NetMapItem item)
            {
                if (item == netMapItem)
                    throw new ArgumentException();
                if (item._NetMapControl != netMapItem._NetMapControl)
                    throw new ArgumentException();
                if (!netMapItem._DownList.Contains(item))
                {
                    netMapItem._DownList.Add(item);
                    item._UpList.Add(netMapItem);
                    netMapItem.OnDownAdded(item);
                }

            }
            public void Remove(NetMapItem item)
            {
                if (item == netMapItem)
                    throw new ArgumentException();
                if (item._NetMapControl != netMapItem._NetMapControl)
                    throw new ArgumentException();
                if (netMapItem._DownList.Contains(item))
                {
                    netMapItem._DownList.Remove(item);
                    item._UpList.Remove(netMapItem);
                    netMapItem.OnDownRemoved(item);
                }
            }
            public void Clear()
            {
                foreach (NetMapItem item in netMapItem._DownList.ToArray())
                {
                    netMapItem._DownList.Remove(item);
                    item._UpList.Remove(netMapItem);
                    netMapItem.OnDownRemoved(item);
                }
            }

            public int Count
            {
                get
                {
                    return this.netMapItem._DownList.Count;
                }
            }
        }
    }
}
